#ifndef _COMMON_LIBS_H
#define _COMMON_LIBS_H

#include <stdarg.h>
#include <dirent.h>
#include <maxmod9.h>

// common headers

// local headers
#include "common_library.h"
#include "common_library_type.h"
#include "version.h"

// library pointers structure
#define LIBS_VERSION MAKE_VERSION(0, 1, 0, STATE_ALPHA, 1)
struct sLibs {
    ver_t version;

    sStd *std;  // standard functions
    sNds *nds;  // libnds functions
    sX *x;      // xlibrary functions
    sF *f;      // file functions
    sFL *fl;    // filelist functions
    sLL *ll;    // linked list functions
    sLF *lf;    // lifo functions
    sAPI *api;  // SunOS API functions
    sMM *mm;    // maxmod functions
};

#undef feof
#undef ferror
#define bzero(a, b) memset(a, 0, b)

// standard functions
#define STD_VERSION MAKE_VERSION(0, 9, 0, STATE_ALPHA, 1)
struct sStd {
    ver_t version;

    char *__ctype_ptr__;    // for isalpha, isupper, ....
    void (*__aeabi_idiv)();
    void (*__aeabi_idivmod)();
    void (*__aeabi_uidiv)();
    void (*__aeabi_ldivmod)();

    int             (*iprintf)      (const char *, ...);
    int             (*printf)       (const char *, ...);
    int             (*sprintf)      (char *, const char *, ...);
    int             (*fprintf)      (FILE *, const char *, ...);
    int             (*viprintf)     (const char *, va_list);
    int             (*vprintf)      (const char *, va_list);
    int             (*vsprintf)     (char *, const char *, va_list);
    int             (*vfprintf)     (FILE *, const char *, va_list);
    int             (*sscanf)       (const char *, const char *, ...);

    int             (*remove)       (const char *);
    int             (*mkdir)        (const char *, mode_t);
    int             (*chdir)        (const char *);
    char *          (*getcwd)       (char *, size_t);
    int             (*rename)       (const char *, const char *);
    DIR *           (*opendir)      (const char *);
    struct dirent * (*readdir)      (DIR *);
    int             (*closedir)     (DIR *);
    int             (*stat)         (const char *, struct stat *);

    size_t          (*strlen)       (const char *);
    char *          (*strcpy)       (char *, const char *);
    char *          (*strncpy)      (char *, const char *, size_t);
    int             (*strcmp)       (const char *, const char *);
    int             (*strcasecmp)   (const char *, const char *);
    int             (*strncmp)      (const char *, const char *, size_t);
    char *          (*strcat)       (char *, const char *);
    char *          (*strchr)       (const char *, int);
    char *          (*strrchr)      (const char *, int);
    char *          (*strstr)       (const char *, const char *);
    char *          (*endName)      (char, char *);
    void *          (*memcpy)       (void *, const void *, size_t);
    void *          (*memset)       (void *, int, size_t);
    void *          (*memchr)       (const void *, int c, size_t n);
    int             (*memcmp)       (const void *, const void *, size_t n);
    void *          (*alloca)       (size_t);

    int             (*atoi)         (const char *);
    float           (*atof)         (const char *);
};

// libnds functions
#define NDS_VERSION MAKE_VERSION(0, 3, 1, STATE_ALPHA, 1)
struct sNds {
    ver_t version;

    void            (*swiWaitForVBlank) (void);
    void            (*scanKeys)         (sKeysInstance *ki);
    uint32          (*keysHeld)         (sKeysInstance *ki);
    uint32          (*keysDown)         (sKeysInstance *ki);
    uint32          (*keysDownRepeat)   (sKeysInstance *ki);
    void            (*keysSetRepeat)    (sKeysInstance *ki, u8 setDelay, u8 setRepeat);
    uint32          (*keysUp)           (sKeysInstance *ki);
    uint32          (*keysCurrent)      (void);
    void            (*touchReadXY)      (touchPosition *data);

    // trigo
    s16             (*cos)              (s16 angle);
    s16             (*sin)              (s16 angle);
    s32             (*tan)              (s16 angle);
};

// xlibrary functions
#define X_VERSION MAKE_VERSION(0, 3, 0, STATE_ALPHA, 1)
struct sX {
    ver_t version;

// xmalloc.h
    void *  (*_xmalloc)             (size_t size, const char *text, u32 line);
    void *  (*xmalloc)              (size_t size);
    void *  (*_xcalloc)             (size_t num, size_t size, const char *text, u32 line);
    void *  (*xcalloc)              (size_t num, size_t size);
    void *  (*_xrealloc)            (void *ptr, size_t size, const char *text, u32 line);
    void *  (*xrealloc)             (void *ptr, size_t size);
    void    (*_xfree)               (void *ptr, const char *text, u32 line);
    void    (*xfree)                (void *ptr);

// xstring.h
    char *  (*endName)              (char delimiter, char *path);
    int     (*cmpext)               (char *fn, char *ext);
    char *  (*xstrdup)              (char *str);
    void *  (*xmemdup)              (void *data, u32 dataSize);

// xmem.h
    size_t  (*getMemFree)           ();
};

// file functions
#define F_VERSION MAKE_VERSION(0, 1, 0, STATE_ALPHA, 1)
struct sF {
    ver_t version;

    FILE *  (*open)     (const char *, const char *);
    int     (*seek)     (FILE *, long int, int);
    long    (*tell)     (FILE *);
    void    (*rewind)   (FILE *);
    size_t  (*read)     (void *, size_t, size_t, FILE *);
    int     (*getc)     (FILE *);
    char *  (*gets)     (char *, int, FILE *);
    size_t  (*write)    (const void *, size_t, size_t, FILE *);
    int     (*putc)     (int, FILE *);
    int     (*puts)     (const char *, FILE *);
    int     (*eof)      (FILE *);
    int     (*error)    (FILE *);
    int     (*close)    (FILE *);
};

// file list functions
#define FL_VERSION MAKE_VERSION(0, 1, 0, STATE_ALPHA, 1)
struct sFL {
    ver_t version;

    sFile * (*getcwd)   (u32 flags, ...);
    sFile * (*readcwd)  (u32 flags);
    int     (*sort)     (sFile *fl, u32 flags, ...);
    u32     (*hash)     (char *fn);
    void    (*free)     (sFile *fl);
};

// linked list functions (xlibrary/llist.h)
#define LL_VERSION MAKE_VERSION(0, 1, 0, STATE_ALPHA, 1)
struct sLL {
    ver_t version;

    LLIST * (*new_el)            (void *data);
    LLIST * (*append_el)         (LLIST **, void *data);
    bool    (*is_loop)           (LLIST *);
    LLIST * (*get_first_el)      (LLIST *);
    LLIST * (*get_nth_el)        (LLIST *, u32 num);
    LLIST * (*get_last_el)       (LLIST *);
    u32     (*get_num_el)        (LLIST *);
    void    (*for_each_data)     (LLIST *, datahandler);
    LLIST * (*search_data)       (LLIST *, void *data);
    void    (*sort)              (LLIST *, datacmp);
    void    (*free_el)           (LLIST *);
    void    (*free_all)          (LLIST *);
    void    (*free_all_handler)  (LLIST *, datahandler);
};

// lifo functions (xlibrary/lifo.h)
#define LF_VERSION MAKE_VERSION(0, 1, 0, STATE_ALPHA, 1)
struct sLF {
    ver_t version;

    LIFO *  (*new)		    ();
    bool	(*is_valid)		(LIFO *);
    void	(*push)	    	(LIFO *, void *);
    u32		(*get_num_el)	(LIFO *);
    void *  (*pop)  		(LIFO *);
    void	(*free)			(LIFO *);
    void    (*free_handler) (LIFO *, datahandler);
};

// SunOS functions
#define API_VERSION MAKE_VERSION(0, 7, 0, STATE_ALPHA, 1)
struct sAPI {
    ver_t version;

    sLibResult  (*getLibraryByFileExt)      (char *fn, enum LibraryType type);  // type == 0 to search in all types
    sLibResult  (*getLibraryByFileType)     (char *fn, enum LibraryType type);	// give a path to a file (without the extension) and a type and it'll return the library
    sLibResult  (*getLibraryByFileType_cfs) (sFile *cf, char *bn, enum LibraryType type);
    sLibResult  (*getLibraryById)           (u32 id);
    sLibProc *  (*newLibInstance)       (sLibrary *lib);
    void        (*deleteLibInstance)    (sLibProc *lp);

    sPlugin *   (*getPluginByExt)       (char *fn);
    int         (*runPlugin)            (sPlugin *plug, int argc, char *argv[]);
};

// maxmod functions
#define MM_VERSION MAKE_VERSION(0, 1, 0, STATE_BETA, 1)
struct sMM {
    ver_t version;

    u8 *    isStreaming;
    int     (*StreamOpen)   (mm_stream* stream);
    void    (*StreamUpdate) ();
    void    (*StreamClose)  ();
};

#endif

